using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using OFX.Aggregator.Infrastructure;
using OFX.Aggregator.WebScraping.Infrastructure;

namespace OFX.Aggregator.WebScraping.Implementation
{
	public class WebStatementRetrievalAsyncResult : Infrastructure.WebStatementRetrievalAsyncResult
	{
		public WebStatementRetrievalAsyncResult(
			AsyncCallback callback, IStatementProcessingWorkflow workflow,
			string initialAddress,
			IHtmlPageProcessor[] htmlProcessors,
			IProgressReport progressReport)
			: base(callback, workflow, htmlProcessors)
		{
			this.statementSource = workflow.StatementSource;
			this.progressReport = progressReport;

			if (progressReport != null)
				progressReport.OnProgress(CurrentStageIndex, HtmlProcessors.Length);

			this.webBrowser.Navigate(initialAddress);
			this.webBrowser.DocumentCompleted += webBrowser_DocumentCompleted;

			if (this.webBrowser.ReadyState == WebBrowserReadyState.Complete)
			{
				ProcessPage(this.webBrowser);
			}
		}

		#region Overrides

		public override Exception Exception
		{
			protected set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				base.Exception = value;

				if (this.progressReport != null)
					this.progressReport.OnFinish();

				this.webBrowser.Dispose();
			}
		}

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <filterpriority>2</filterpriority>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				this.webBrowser.Dispose();
			}

			base.Dispose(disposing);
		}

		#endregion

		#region Private Methods

		private void ProcessPage(WebBrowser browser)
		{
			IHtmlPageProcessor processor = CurrentProcessor;

			if (processor == null)
				return;

			try
			{
				using (var webBrowserWrapper = new WebBrowserWrapper(browser))
				{
					processor.TestAssumptions(this.statementSource, webBrowserWrapper);
					processor.ProcessPage(this.statementSource, webBrowserWrapper, this);
				}

				++CurrentStageIndex;

				if (this.progressReport != null)
					this.progressReport.OnProgress(CurrentStageIndex, HtmlProcessors.Length);

				if (CurrentProcessor == null)
				{
					browser.DocumentCompleted -= webBrowser_DocumentCompleted;

					if (this.progressReport != null)
						this.progressReport.OnFinish();
				}
			}
			catch (ArgumentException argumentEx)
			{
				HandleException(argumentEx);
			}
			catch (AssumptionFailedException assumptionFailedEx)
			{
				HandleException(assumptionFailedEx);
			}
			catch (COMException comEx)
			{
				HandleException(comEx);
			}
		}

		#endregion

		#region Private Fields

		private readonly IProgressReport progressReport;
		private readonly StatementSource statementSource;
		private readonly WebBrowser webBrowser = new WebBrowser();

		#endregion

		#region Event Handlers

		private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
		{
			var senderWebBrowser = (WebBrowser) sender;

			if (senderWebBrowser.ReadyState == WebBrowserReadyState.Complete)
			{
				Debug.Assert(senderWebBrowser.Document != null);

				ProcessPage(senderWebBrowser);
			}
		}

		#endregion
	}
}